/////////////////////////////////////////////////////////////////////////////
// CAdapter class implements a simple wrapper for winpcap functions.
//
// Version 1.0 - 15 june 2007
//
// Copyright (c) 2007 by FlexByte Software (http://flexbyte.com)
// All rights reserved.
//
// You may use the source code in any product (freeware, commercial, etc.). 
// You may modify the source code as you want except 
// you cannot modify the copyright details at the top of each module.
// If you have a web site or blog give a link to our web site, flexbyte.com
/////////////////////////////////////////////////////////////////////////////


#include "adapter.h"

#include <assert>
#pragma comment( lib, "wpcap.lib" )
#pragma comment( lib, "packet.lib" )
#pragma message( "   Automatically linking with winpcap library" )
/////////////////////////////////////////////////////////////////////////////
//#define	PCAP_OPENFLAG_PROMISCUOUS	( 1 )
/////////////////////////////////////////////////////////////////////////////
CAdapter::CAdapter(IPacketAnalyzer * pAnalyzer)
: m_hDevice( NULL )
, m_pAnalyzer( pAnalyzer )
{
}
/////////////////////////////////////////////////////////////////////////////
CAdapter::~CAdapter(void)
{
	Close();
}
void CAdapter::SetName()
{
	THREADNAME_INFO info;
	info.dwType = 0x1000;
	info.szName = "AdapterThread";
	info.dwThreadID = -1;
	info.dwFlags = 0;

	__try
	{
		RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD),(DWORD*)&info );
	}
	__except (EXCEPTION_CONTINUE_EXECUTION)
	{
	}
}
/////////////////////////////////////////////////////////////////////////////
void CAdapter::Open(
	const char * szDeviceName,
	int iBufferMax/*no limit*/,
	int iPacketSize/* = 0xFFFF*/,
	int iTimeout/* = 100*/,
	bool bPromisc/* = false*/
	)
{
	assert( szDeviceName );
	if( m_hDevice )
		Close();

	m_iBufferMax = iBufferMax;

	char szErrorMsg[ PCAP_ERRBUF_SIZE ];
	// Try to open device with promiscuous mode
	m_hDevice = pcap_open_live(
		szDeviceName,
		iPacketSize,
		( bPromisc ) ? PCAP_OPENFLAG_PROMISCUOUS : 0,
		iTimeout,
		szErrorMsg
		);
	if( !m_hDevice )
		throw CFlexException( szErrorMsg, true );

	m_sDeviceName = szDeviceName;
	m_sDeviceType = pcap_datalink_val_to_description( GetDataLink() );
	// Run capture thread
	m_thread.Execute( this );
}
/////////////////////////////////////////////////////////////////////////////
void CAdapter::Close()
{
	m_bWork = false;
	m_thread.Exit();
	if( m_hDevice )
	{
		pcap_close( m_hDevice );
		m_hDevice = NULL;
	}
}
/////////////////////////////////////////////////////////////////////////////
void CAdapter::SetFilter(char * szFilter, u_int netmask/* = 0xFFFFFFFF*/)
{
	struct bpf_program bpf;

	// Create bpf filter
	int rc = pcap_compile( m_hDevice, &bpf, szFilter, 1, netmask );
	if( rc < 0 )
		throw CFlexException( pcap_geterr( m_hDevice ), true );

	// Now set bpf filter
	rc = pcap_setfilter( m_hDevice, &bpf );
	if( rc < 0 )
		throw CFlexException( pcap_geterr( m_hDevice ), true );

	pcap_freecode( &bpf );
}
/////////////////////////////////////////////////////////////////////////////
int CAdapter::GetDataLink() const
{
	assert( m_hDevice );
	return pcap_datalink( m_hDevice );
}
/////////////////////////////////////////////////////////////////////////////
int CAdapter::GetPacketOffset()
{
#ifndef WIN32 
	switch( GetDataLink() )
	{
	case DLT_EN10MB:	// Ethernet
	case DLT_IEEE802:	// Token Ring
		return 14; // sizeof( Ethernet header )
	case DLT_SLIP:
		return 16;
	case DLT_NULL:
	case DLT_PPP:
		return 4;
	case DLT_ATM_RFC1483:
		return 8;
	case DLT_RAW:
		return 0;
	default:
		assert( 0 ); // not implemented
	}
#endif
	return 14; // sizeof( Ethernet header )
}
/////////////////////////////////////////////////////////////////////////////
void CAdapter::Execute()
{
	SetName();
	int iPacketOffset = GetPacketOffset();
	m_bWork = true;
	int ibuffer = 0;
	m_pAnalyzer->OnAnalyzeStart();
	while( m_bWork )
	{
		//CFlexAutoLock lock( &m_lock );
		Packet pck;
		int rc = pcap_next_ex( m_hDevice, &pck._header, &pck._data );
		if( !rc )
			continue;
		if( rc < 0 )
			break;
		pck._offset = iPacketOffset;
		m_pAnalyzer->AnalyzePacket( pck );

		ibuffer += pck._header->len;
		if(ibuffer > m_iBufferMax)
		{
			break;
        }
	}
	m_bWork = false;
	m_pAnalyzer->OnAnalyzeStop();
	TRACE( "[THREAD EXIT] CAdapter::Execute()\n" );
}
/////////////////////////////////////////////////////////////////////////////
void CAdapter::Break()
{
	//CFlexAutoLock lock( &m_lock );
	m_bWork = false;

	if( m_hDevice )
		pcap_breakloop( m_hDevice );
	m_thread.MsgWait();
}
/////////////////////////////////////////////////////////////////////////////
